using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ServiceModel;
using System.Threading.Tasks;
using Director.DomainModels;

namespace Director
{
	[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, IncludeExceptionDetailInFaults = true)]
	public class DirectorService : IDirectorService
	{
		private readonly MyConnector _myConnector;
		readonly List<OperationContext> _contexts = new List<OperationContext>();
		//private OperationContext _operationContext;

		readonly List<Task> _tscList = new List<Task>();
		readonly object _syncroot = new object();

		public DirectorService()
		{
			_myConnector = new MyConnector(StateChangedAction, LogAction, ErrorAction);

			TXmlConnector.DataHolder.CandlesUpdatedAction = CandlesUpdatedAction;
			TXmlConnector.DataHolder.DataReceivedAction = DataReceivedAction;
			TXmlConnector.DataHolder.OnNewTradeAction = OnNewTradeAction;
			TXmlConnector.DataHolder.OnNewOrderAction = OnNewOrderAction;
		}

		private void CallBack(Action<IDirectorServiceCallback> act)
		{
			var contexts = _contexts.ToArray();
			foreach (var operationContext in contexts)
			{
				var oc = operationContext;
				if (oc != null)
				{
					try
					{
						var callbackChannel = oc.GetCallbackChannel<IDirectorServiceCallback>();
						act(callbackChannel);
					}
					catch (Exception ex)
					{
						if (Debugger.IsAttached)
						{
							Debugger.Break();
						}
						_contexts.Remove(oc);
						oc.Channel.Closed += ChannelOnClosed;
						oc.Channel.Faulted += ChannelOnFaulted;
					}
				}
			}
		}


		private void OnNewTradeAction(Trade trade)
		{
			CallBack(s => s.OnNewTrade(trade));
		}


		private void OnNewOrderAction(Order order)
		{
			CallBack(s => s.OnNewOrder(order));
		}

		private void CandlesUpdatedAction(int securityId, int timeFrameLength)
		{
			CallBack(s => s.CandlesUpdated(securityId, timeFrameLength));
		}
		
		private void StateChangedAction(EServiceState eServiceState)
		{
			CallBack(s => s.ServiceStateChanged(eServiceState));
		}

		private void DataReceivedAction(EDataKind kind, int id)
		{
			CallBack(s => s.DataReceived(kind, id));
		}
		
		private void LogAction(string logMessage)
		{
			CallBack(s => s.OnServiceLog(logMessage));
		}

		private void ErrorAction(string errorMessage)
		{
			CallBack(s => s.OnServiceError(errorMessage));
		}

		public void Disconnect()
		{
			AddTask(() => _myConnector.Disconnect());
		}

		public void Connect()
		{
			AddTask(() => _myConnector.Connect());
		}

		public void RequestHistoryData(string boardId, string secCode, int timeFrameId, bool fixmode)
		{
			AddTask(() => _myConnector.RequestHistoryData(boardId, secCode, timeFrameId, fixmode ? MyConnector.FIX_LENGTH : (long?)null));
		}

		public long Buy(int securityId, int quantity)
		{
			return _myConnector.BuySell(securityId, quantity);
		}

		public long Sell(int securityId, int quantity)
		{
			return _myConnector.BuySell(securityId, -quantity);
		}

		public void Ping()
		{
		}

		public void Subscribe(Security[] securities)
		{
			AddTask(() => _myConnector.Subscribe(securities));
		}

		public void SetMode(EServiceMode mode)
		{
			TXmlConnector.DataHolder.SetMode(mode);
		}

		public IEnumerable<Security> GetSecurities()
		{
			return TXmlConnector.DataHolder.Securities.Values;
		}

		public IEnumerable<TimeFrame> GetTimeFrames()
		{
			return TXmlConnector.DataHolder.TimeFrames.Values;
		}

		public IEnumerable<Market> GetMarkets()
		{
			return TXmlConnector.DataHolder.Markets.Values;
		}

		public IEnumerable<Board> GetBoards()
		{
			return TXmlConnector.DataHolder.Boards.Values;
		}

		public IEnumerable<Position> GetPositions()
		{
			return TXmlConnector.DataHolder.Positions.Values;
		}

		public IEnumerable<Candle> GetCandles(int securityId, int timeframeId, DateTime from)
		{
			return TXmlConnector.DataHolder.ReadCandles(securityId, timeframeId, from);
		}

		public Candle GetLastCandle(int securityId, int timeframeId, DateTime lastOnDate)
		{
			return TXmlConnector.DataHolder.ReadLastCandle(securityId, timeframeId, lastOnDate);
		}

		public EServiceState Hello()
		{
			OC.Channel.Closed += ChannelOnClosed;
			OC.Channel.Faulted += ChannelOnFaulted;
			LogAction("Hello!");
			_myConnector.Hello();
			return _myConnector.IsConnected ? EServiceState.Connected : EServiceState.Disconnected;
		}

		private OperationContext OC
		{
			get
			{
				var operationContext = OperationContext.Current;
				if (!_contexts.Contains(operationContext))
				{
					_contexts.Add(operationContext);
				}
				if (_contexts.Count > 1)
				{

				}
				return operationContext;
			}
		}

		private void ChannelOnFaulted(object sender, EventArgs eventArgs)
		{
			Bye();
		}

		private void ChannelOnClosed(object sender, EventArgs eventArgs)
		{
			Bye();
		}

		public void Bye()
		{
			var oc = OC;
			if (oc != null)
			{
				oc.Channel.Closed -= ChannelOnClosed;
				oc.Channel.Faulted -= ChannelOnFaulted;
			}
		}

		private void AddTask(Action act)
		{
			lock (_syncroot)
			{
				var t = new Task(act);
				t.ContinueWith(ContinuationAction);
				if (_tscList.Count == 0)
				{
					_tscList.Add(t);
					t.Start();
				}
				else
				{
					_tscList.Add(t);
				}
			}
		}

		private void ContinuationAction(Task task)
		{
			lock (_syncroot)
			{
				if (!_tscList.Remove(task))
				{

				}
				var t = _tscList.FirstOrDefault();
				if (t != null)
				{
					t.Start();
				}
			}
		}
	}
}